#include "EventLoop.h"
#include "EpollDispatcher.h"
#include "PollDispatcher.h"
#include "SelectDispatcher.h"

// 实例化主线程的EventLoop: 不可指定线程名
EventLoop::EventLoop() : EventLoop(string()) {}

// 实例化工作线程的EventLoop: 可指定线程名
EventLoop::EventLoop(const string threadName) {
	_isQuit = true;
	// 所属线程
	_threadID = this_thread::get_id();
	_threadName = (threadName == string()) ? "MainThread" : threadName;

	_dispatcher = new EpollDispatcher(this); // 事件分发器
	_channelMap.clear();

	// socketpair
	int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, _socketPair);
	if (ret == -1) {
		LogDebug("socketpair error!");
		exit(0);
	}
	// ·指定规则: evLoop->_socketPair[0]发送数据, evLoop->_sockerPair[1]接收数据
	//			   后者需要添加到[select/poll/epoll监视的fd集合]中，用于唤醒被阻塞的子线程
#if 0
	Channel* channel = new Channel(_socketPair[1], FdEvent::_ReadEvent, readLocalMessage, nullptr, nullptr, this);
#else
	// 将[唤醒线程fd对应的Channel对象]的ADD任务添加到任务队列 -> 将[唤醒线程fd]添加到[select/poll/epoll监视的fd集合]
	// 主线程通过让evLoop->_socketPair[0]发送数据，来激活[唤醒线程fd], 以唤醒被阻塞的子线程
	// 这里的操作要理解清楚
	auto obj = bind(&EventLoop::readMessage, this);
	Channel* channel = new Channel(_socketPair[1], FdEvent::_ReadEvent, obj, nullptr, nullptr, this);
#endif
	addTask(channel, ElemType::ADD);
}

// 析构
EventLoop::~EventLoop() {}

// 激活被阻塞的子线程: 向_socketPair[0]写入数据, _socketPair[0]会给_socketPair[1]发送数据，以激活被阻塞的子线程
void EventLoop::taskWakeup() {
	const char* msg = "激活当前线程";
	write(_socketPair[0], msg, strlen(msg));
}

// _socketPair[1]读取_socketPair[0]发来的数据, 唤醒被阻塞的子线程
int EventLoop::readLocalMessage(void* arg) {
	EventLoop* evLoop = static_cast<EventLoop*>(arg);
	char buffer[256];
	int ret = read(evLoop->_socketPair[1], buffer, sizeof(buffer));
	return ret;
}
int EventLoop::readMessage() {
	char buffer[256];
	int ret = read(_socketPair[1], buffer, sizeof(buffer));
	return ret;
}


// 运行反应堆
int EventLoop::run() {
	_isQuit = false;
	// 1. 判断线程ID是否正常
	if (_threadID != this_thread::get_id()) { 
		LogDebug("%s's eventLoop run error!", _threadName.c_str());
		return -1; 
	}

	// 2. 检测并处理事件
	while (!_isQuit) {
		_dispatcher->dispatch(2); // 事件检测(阻塞等待2s) + 处理就绪事件
		processTask();					 // 处理任务队列中的任务
	}
	return 0;
}

// 处理被激活的fd, events为fd的就绪事件
int EventLoop::eventActive(int fd, int event) {
	if (fd < 0) {
		LogDebug("Wrong Fd!");
		return -1;
	}

	// 1. 取出fd对应的channel
	Channel* channel = _channelMap[fd];
	assert(channel->getFd() == fd);

	// 2. 处理fd的就绪事件
	if (event & static_cast<int>(FdEvent::_ReadEvent) && channel->_readCallback) {
		channel->_readCallback(const_cast<void*>(channel->getArg()));
	}
	if (event & static_cast<int>(FdEvent::_WriteEvent) && channel->_writeCallback) {
		channel->_writeCallback(const_cast<void*>(channel->getArg()));
	}
	return 0;
}


// 添加任务到任务队列: type为任务要对channel执行的操作
int EventLoop::addTask(Channel* channel, ElemType type) {
	// 1. 实例化任务结点，并将任务添加到任务队列
	_mutex.lock();
	ChannelElement* node = new ChannelElement;
	node->_channel = channel;
	node->_type = type;
	_taskQueue.push(node);
	_mutex.unlock();

	// 2. 处理任务
	/* 细节: 前提为当前的EventLoop属于子线程 -> 服务器处于多反应堆模式
	*	(1) 对于结点的添加: 可能是当前子线程, 也可能是主线程 -> 需要互斥锁来保护任务队列
	*		i.  修改fd的监视事件: 一般由当前子线程发起 -> 由当前子线程处理
	*		ii. 添加新的fd: 即和新客户端建立连接, 由主线程发起 -> 由主线程处理
	*	(2) 不能让主线程处理任务队列, 需要由子线程去处理
	*		i.  主线程只负责和客户端建立连接, 连接建立后, 剩下的事情都是由子线程完成 -> 主线程不会处理任务队列的结点
	*		ii. 若服务器处于但反应堆模式，则只有主线程具有EventLoop -> 主线程会处理任务队列的结点
	*/
	if (_threadID == this_thread::get_id()) { processTask(); }
	else { // 当前线程为主线程 -> 通知子线程去处理任务队列中的任务
		/* 此时子线程有2种情况
		*	(1) 子线程正在工作
		*	(2) 子线程被阻塞在select/poll/epoll: 即此时[被检测的fd集合]中没有fd被激活 -> 需要唤醒子线程
		*		·如何唤醒子线程(如何解除阻塞): 在select/poll/epoll检测的fd集合中，添加1个我们可以控制
		*         其读事件是否就绪的[用于唤醒子线程的fd]，[唤醒线程fd]不负责套接字通信 -> 子线程即可正常工作了
		*		·∵需要激活[唤醒线程fd]，∴还需要1个fd给[唤醒线程fd]发送数据
		*/
		taskWakeup(); // 激活被阻塞的工作线程
	}
	return 0;
}

// 处理任务队列中的任务
int EventLoop::processTask() {
	while (!_taskQueue.empty()) {
		// 1. 取出任务队列的头结点
		_mutex.lock();
		ChannelElement* head = _taskQueue.front();
		_taskQueue.pop();
		_mutex.unlock();
		Channel* channel = head->_channel;

		// 2. 处理头结点
		if (head->_type == ElemType::ADD) { add(channel); }
		else if (head->_type == ElemType::DELETE) {
			remove(channel);
			// 将[channel.fd]从[_dispatcher的fd检测集合]中删除, 并销毁Channel及其所属的TcpConnection，
			// 并且会删除记录在eventLoop中的<fd, Channel>
		}
		else if (head->_type == ElemType::MODIFY) { modify(channel); }
		delete head;
	}
	return 0;
}

// 增加任务: 在_channelMap中记录<fd, Channel>，将[channel.fd]添加到[_dispathcer的fd检测集合]中
int EventLoop::add(Channel* channel) {
	int fd = channel->getFd();

	// 1. 记录<fd, Channel>
	if (_channelMap.find(fd) == _channelMap.end()) {
		_channelMap.insert(make_pair(fd, channel));

		// 2. 将[channel.fd]添加到[evLoop.dispathcer的fd检测集合]中
		_dispatcher->setChannel(channel);
		int ret = _dispatcher->add();
		return ret;
	}
	return -1;
}

/* 删除任务
// 1. 将[channel.fd]从[_dispatcher的fd检测集合]中删除
// 2. 销毁Channel及其所属的TcpConnection，
// 3. 删除记录在eventLoop中的<fd, Channel>
*/
int EventLoop::remove(Channel* channel) {
	// 若channelMap没有记录该fd -> dispathcer的fd检测集合中没有该fd -> 直接返回即可
	if (_channelMap.find(channel->getFd()) == _channelMap.end()) { return -1; }

	// 将[channel.fd]从[_dispatcher的fd检测集合]中删除, 并销毁Channel及其所属的TcpConnection，
	// 并且会删除记录在eventLoop中的<fd, Channel>
	_dispatcher->setChannel(channel);
	int ret = _dispatcher->remove();
	return ret;
}

// 修改任务: 将[_dispatcher的fd检测集合]中[channel.fd]的监视事件修改为[channel.event]
int EventLoop::modify(Channel* channel) {
	// 若在[dispatcher的fd检测集合]中找不到该fd，则直接返回
	if (_channelMap.find(channel->getFd()) == _channelMap.end()) { return -1; }

	// 修改[evLoop.dispatcher的fd检测集合]中channel对应fd的属性
	_dispatcher->setChannel(channel);
	int ret = _dispatcher->modify();
	return ret;
}

// 删除记录在_channelMap中的<fd, channel> + 关闭fd + 释放channel
int EventLoop::destroyChannel(Channel* channel) {
	auto it = _channelMap.find(channel->getFd());
	if (it != _channelMap.end()) {
		_channelMap.erase(it);   // 删除channel.fd和channel的对应关系
		close(channel->getFd()); // 关闭channel.fd
		delete channel;          // 释放channel
	}
	return 0;
}
